package com.mico.xchange.bitz.service;

import java.beans.Encoder;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import javax.crypto.Mac;
import javax.ws.rs.QueryParam;

import com.mico.xchange.bitz.BitZAuthenticated;
import com.mico.xchange.service.BaseParamsDigest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import si.mazi.rescu.Params;
import si.mazi.rescu.RestInvocation;

import static com.mico.xchange.utils.DigestUtils.bytesToHex;

public class BitZDigest extends BaseParamsDigest{
  private static final Logger LOG = LoggerFactory.getLogger(BitZDigest.class);

  private final Field invocationUrlField;
  private String secretKey1;

  public BitZDigest(String secretKey){
    super(secretKey, HMAC_MD5);
    try {
      secretKey1 = secretKey;
      invocationUrlField = RestInvocation.class.getDeclaredField("invocationUrl");
      invocationUrlField.setAccessible(true);
    } catch (NoSuchFieldException e) {
      throw new IllegalStateException("rescu library has been updated");
    }
  }

  // TODO: Handle Exception
  public static BitZDigest createInstance(String secretKey) {
    try {
      return new BitZDigest(secretKey);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return null;
  }

  /** @return the query string except of the "signature" parameter */
  private static String getQuery(RestInvocation restInvocation) {
    final Params p = Params.of();
    restInvocation
            .getParamsMap()
            .get(QueryParam.class)
            .asHttpHeaders()
            .entrySet()
            .stream()
            .filter(e -> !BitZAuthenticated.SIGNATURE.equals(e.getKey()))
            .forEach(e -> p.add(e.getKey(), e.getValue()));
    return p.asQueryString();
  }

  @Override
  public String digestParams(RestInvocation restInvocation) {
    try {
      final String input;

      if (restInvocation.getPath().startsWith("wapi/")) {
        // little dirty hack for /wapi methods
        input = getQuery(restInvocation);
      } else {
        switch (restInvocation.getHttpMethod()) {
          case "GET":
          case "DELETE":
            input = getQuery(restInvocation);
            break;
          case "POST":
            input = restInvocation.getRequestBody();
            break;
          default:
            throw new RuntimeException(
                    "Not support http method: " + restInvocation.getHttpMethod());
        }
      }



      Mac mac = getMac();
      String signStr = sortSource(input) + secretKey1;

      String encodeStr = URLEncoder.encode(signStr,"UTF-8");

      String sign = getMD5Str(signStr);
      LOG.debug("value to sign: {},  signature: {}", signStr, sign);

      String invocationUrl = restInvocation.getInvocationUrl();
      LOG.debug("old invocationUrl: {}", invocationUrl);

      final String sig = "sign=";
      int idx = invocationUrl.indexOf("?");
      String newInvocationUrl = invocationUrl.substring(0, idx+1) +sortSource(input)+"&sign="+sign;
      try {
        invocationUrlField.set(restInvocation, newInvocationUrl);
      } catch (IllegalArgumentException | IllegalAccessException e) {
        throw new RuntimeException(e);
      }
      LOG.debug("new invocationUrl: {}", restInvocation.getInvocationUrl());
      restInvocation.getAllHttpHeaders().put("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36");
      return sign;
    } catch (Exception e) {
      throw new RuntimeException("Illegal encoding, check the code.", e);
    }
  }


  public String sortSource(String input){
    StringBuffer sb = new StringBuffer();
    String[] strings = input.split("&");
    Map<String,String> map = new HashMap<String,String>();
    for(String str : strings){
      String[] pair = str.split("=");
      map.put(pair[0],pair[1]);
    }
    Collection<String> keyset= map.keySet();

    List list=new ArrayList<String>(keyset);

    Collections.sort(list);
    //这种打印出的字符串顺序和微信官网提供的字典序顺序是一致的
    for(int i=0;i<list.size();i++){
      if(0!=i){
        sb.append("&");
      }
      sb.append(list.get(i)+"="+map.get(list.get(i)));
    }
    return sb.toString();
  }

  private static String getMD5Str(String str)
  {
    MessageDigest messageDigest = null;
    try
    {
      messageDigest = MessageDigest.getInstance("MD5");
      messageDigest.reset();
      messageDigest.update(str.getBytes("UTF-8"));
    } catch (NoSuchAlgorithmException e)
    {
      System.out.println("NoSuchAlgorithmException caught!");
      System.exit(-1);
    } catch (UnsupportedEncodingException e)
    {
      e.printStackTrace();
    }

    byte[] byteArray = messageDigest.digest();

    StringBuffer md5StrBuff = new StringBuffer();

    for (int i = 0; i < byteArray.length; i++)
    {
      if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
        md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
      else
        md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
    }
    return md5StrBuff.toString();
  }

  public static void main(String[] args){
    String input = "api_key=958f94d68c7873109740914407491fa1&nonce=996126&timestamp=1527306993308dmIFIZ8GtIMwueiLZrCtMYxLkwRaz0f0E7EILMwQyg8tvQkj8UVULrUAfkf4I4jm";
    System.out.println(getMD5Str(input));
    System.out.println("59793f83d3edfa0648e927153354dccb");
  }
}
